//
//  UIView+Extension.swift
//  Skeleton
//
//  Created by bin hu on 2025/6/4.
//

import UIKit

private var skeletonLayersKey: UInt8 = 001
private var excludedViewsKey: UInt8 = 002
private var layerPoolKey: UInt8 = 003
private var skeletonEnableKey: UInt8 = 004
private var skeletonConfigKey:UInt8 = 009

extension UIView:SkeletonDisplayable {
    
//    @IBInspectable
    /// 控制骨架屏启用的布尔属性
    var skeletonEnable: Bool {
        get {
            return objc_getAssociatedObject(self, &skeletonEnableKey) as? Bool ?? false
        }
        set {
            objc_setAssociatedObject(self, &skeletonEnableKey, newValue, .OBJC_ASSOCIATION_RETAIN)
            if newValue {
                showSkeleton()
            }else{
                hideSkeleton()
            }
        }
    }

    private var skeletonLayers: [String: CALayer] {
        get { objc_getAssociatedObject(self, &skeletonLayersKey) as? [String: CALayer] ?? [:] }
        set { objc_setAssociatedObject(self, &skeletonLayersKey, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
    
    private var excludedViews: Set<UIView> {
        get { objc_getAssociatedObject(self, &excludedViewsKey) as? Set<UIView> ?? [] }
        set { objc_setAssociatedObject(self, &excludedViewsKey, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
    
    private var layerPool: SkeletonLayerPool {
        get {
            if let pool = objc_getAssociatedObject(self, &layerPoolKey) as? SkeletonLayerPool {
                return pool
            }
            let newPool = SkeletonLayerPool()
            objc_setAssociatedObject(self, &layerPoolKey, newPool, .OBJC_ASSOCIATION_RETAIN)
            return newPool
        }
    }
    
    // 存储配置
    private var skeletonConfig: SkeletonConfig {
        get { objc_getAssociatedObject(self, &skeletonConfigKey) as? SkeletonConfig ?? SkeletonConfig() }
        set { objc_setAssociatedObject(self, &skeletonConfigKey, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
    
    public func showSkeleton(config:SkeletonConfig? = nil) {
        hideSkeleton()
        self.skeletonConfig = config ?? SkeletonConfig()
        addSkeletonsRecursively(for: self)
        startAnimations()
    }
    
    public func hideSkeleton() {
        stopAnimations()
        removeAllSkeletons()
    }
    
    func excludeSkeletonView(_ view: UIView) {
        excludedViews.insert(view)
    }
    
    /// 跳过排除的视图和隐藏视图
    private func addSkeletonsRecursively(for view: UIView) {
        guard !excludedViews.contains(view),
              !view.isHidden,
              view.alpha > 0.01,
              view.frame.width > 0 && view.frame.height > 0 else {
            return
        }
        
        let depth = viewDepth(view)
        let zPosition = skeletonConfig.baseZPosition + depth * skeletonConfig.depthFactor
        let identifier = "\(ObjectIdentifier(view).hashValue)"
        
        addSkeletonLayer(for: view, identifier: identifier, zPosition: zPosition)
        
        /// 如果是复合控件（不递归内部）
        guard !isCompositeControl(view) else { return }
        
        /// 递归处理所有可见子视图
        for subview in view.subviews {
            addSkeletonsRecursively(for: subview)
        }
    }
    
    private func viewDepth(_ view: UIView) -> CGFloat {
        var depth: CGFloat = 0
        var current: UIView? = view.superview
        while current != nil {
            depth += 1
            current = current?.superview
        }
        return depth
    }
    
    private func addSkeletonLayer(for view: UIView, identifier: String, zPosition: CGFloat) {
        // 确保视图在视图层级中
        guard view.superview != nil else { return }
        
        let frame = view.convert(view.bounds, to: self)
        // 跳过无效或不可见的frame
        guard !frame.isInfinite, !frame.isNull,
              frame.width > 0, frame.height > 0,
              frame.maxX > 0, frame.maxY > 0 else {
            return
        }
        
        let skeletonLayer = layerPool.dequeueLayer()
        configureSkeletonLayer(skeletonLayer,
                               frame: frame,
                               depth: zPosition,
                               view: view)
        skeletonLayer.zPosition = zPosition
        layer.addSublayer(skeletonLayer)
        skeletonLayers[identifier] = skeletonLayer
    }
    
    private func configureSkeletonLayer(_ layer: CALayer,
                                        frame: CGRect,
                                        depth: CGFloat,
                                        view:UIView) {

        layer.frame = frame
        
        let depthFactor = min(1.0, max(0.2, (depth - skeletonConfig.baseZPosition) / 100))
        let baseColor = skeletonConfig.baseColor.withBrightnessDelta(-depthFactor * 0.1)
        let highlight = skeletonConfig.highlightColor.withBrightnessDelta(-depthFactor * 0.1)
        
        if let gradientLayer = layer as? CAGradientLayer {
            gradientLayer.colors = [baseColor.cgColor,
                                    highlight.cgColor,
                                    baseColor.cgColor]
            gradientLayer.drawsAsynchronously = true
            gradientLayer.locations = [0, 0.5, 1]
            gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
            gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
        }
        
        layer.cornerRadius = skeletonConfig.cornerRadius - depthFactor * 2
        layer.masksToBounds = true
    }
    
    private func removeAllSkeletons() {
        for (_, layer) in skeletonLayers {
            layer.removeFromSuperlayer()
            layerPool.enqueueLayer(layer)
        }
        skeletonLayers.removeAll()
    }
    
    private func startAnimations() {
        let animation = CABasicAnimation(keyPath: "locations")
        animation.fromValue = [-1.0, -0.5, 0.0]
        animation.toValue = [1.0, 1.5, 2.0]
        animation.duration = skeletonConfig.animationDuration
        animation.repeatCount = .infinity
        animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        
        // 分层动画偏移，避免所有动画同时开始
        for (index, layer) in skeletonLayers.values.enumerated() {
            let delay = skeletonConfig.animationDelayDepthFactor * Double(index % 3)
            DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                layer.add(animation, forKey: "skeletonWave")
            }
        }
    }
    
    private func stopAnimations() {
        skeletonLayers.values.forEach { $0.removeAnimation(forKey: "skeletonWave") }
    }
    
    /// 判断是否为复合控件（不递归内部）
    private func isCompositeControl(_ view: UIView) -> Bool {
        // 需要整体处理的控件类型
        let compositeTypes: [UIView.Type] = [
            UIButton.self,
            UITextField.self,
            UISwitch.self,
            UISlider.self,
            UISegmentedControl.self,
            UIActivityIndicatorView.self,
            UIProgressView.self,
            UIDatePicker.self
        ]
        
        return compositeTypes.contains { type(of: view) == $0 }
    }

}
